<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>使用Promise + XHR封装ajax请求函数</title>
</head>
<body>
  <button onclick="testGet()">发送GET请求</button><br>
  <button onclick="testPost()">发送POST请求</button><br>
  <button onclick="testPut()">发送PUT请求</button><br>
  <button onclick="testDelete()">发送Delete请求</button><br>

  <script>
    /*
    1.函数的返回值为promise, 成功的结果为response, 异常的结果为error
    2.能处理多种类型的请求: GET/POST/PUT/DELETE
    3.函数的参数为一个配置对象
    {
    url: '',   // 请求地址
    method: '',   // 请求方式GET/POST/PUT/DELETE
    params: {},  // GET/DELETE请求的query参数
    data: {}, // POST或PUT请求的请求体参数 
    }
    响应json数据自动解析为js对象/数组
    */

    /*
    open()中接收请求方法、请求地址以及是否异步
    GET方式和DELETE方式不在请求体中发送,通过query发送请求
    但是POST和PUT方式在请求体中发送,data
    */

    function axios({
      url,
      method='GET',
      params={},
      data={}
    }) {
      // 如果是GET和DELETE,通过query在url上以'?id=0'的形式拼接参数
      let queryStr = '' 
      // params ===> {a: 1, b: 2} ===> a=1&b=2
      Object.keys(params).forEach(key => {
        // 通过Object.keys()方法拿到对象里每个属性名
        queryStr = `&${key}=${params[key]}` // &a=1&b=2
      })
      if(queryStr) {
        queryStr = queryStr.substring(1) // 将queryStr中多出来的&去除 a=1&b=2
        url += '?' + queryStr
      }

      // 将data转换为字符串
      data = JSON.stringify(data)

      // 函数的返回值为promise
      return new Promise((resolve, reject) => {
        // 1.发送Ajax请求
        // 1.1创建xhr对象
        const xhr = new XMLHttpRequest()
        // 1.2初始化
        xhr.open(method, url, true)
        // 1.3发送请求(请求体)
        if(method === 'POST' || method === 'PUT') {
          xhr.setRequestHeader('Content-Type', 'application/json;charset=utf-8') // 告诉服务器请求体的格式是json
          xhr.send(data) // POST和PUT方式在请求体中发送,data
        } else {
          xhr.send() // GET方式和DELETE方式不在请求体中发送所以不用传值
        }
        // 1.4绑定监听改变状态返回对应的结果 
        xhr.onreadystatechange = function() {
          if(xhr.readyState !== 4) return // 说明请求还未完成直接退出
          if(xhr.status >= 200 && xhr.status < 299) {
            const response = {
              data: JSON.parse(xhr.response),
              status: xhr.status,
              statusText: xhr.statusText
            }

            resolve(response) // 成功调用resolve改变状态,传入成功的response
          } else {
            reject(new Error('request error status is' + xhr.status)) // 失败调用reject改变状态,传入失败的error
          }
        }
      })
    }
  </script>

  <script>
    function testGet() {
      axios({ // 配置对象: 属性名是一些特殊名称
        method: 'GET',
        url: 'http://localhost:3000/posts',
        params: { // 指定query参数,而不是params参数
          id: 2
        }
      }).then(
        response => {
          console.log(response.data, response.status, response.statusText)
        },
        error => {
          alert(error.message)
        }
      )
    }

    function testPost() {
      axios({ // 配置对象: 属性名是一些特殊名称
        method: 'POST',
        url: 'http://localhost:3000/posts',
        data: { // 指定请求头参数数据
          title: 'ccc',
          author: 'ddd'
        }
      }).then(
        response => {
          console.log(response.data, response.status, response.statusText)
        },
        error => {
          alert(error.message)
        }
      )
    }

    function testPut() {
      axios({ // 配置对象: 属性名是一些特殊名称
        method: 'PUT',
        url: 'http://localhost:3000/posts/6',
        data: { // 指定请求头参数数据
          title: '经济金融',
          author: '王博文'
        }
      }).then(
        response => {
          console.log(response.data, response.status, response.statusText)
        },
        error => {
          alert(error.message)
        }
      )
    }

    function testDelete() {
      axios({ // 配置对象: 属性名是一些特殊名称
        method: 'DELETE',
        url: 'http://localhost:3000/posts/6',
      }).then(
        response => {
          console.log(response.data, response.status, response.statusText)
        },
        error => {
          alert(error.message)
        }
      )
    }
  </script>
</body>
</html>